#include "../../include/server/chatservice.hpp"
#include "public.hpp"
#include "chatservice.hpp"
#include <muduo/base/Logging.h>
#include <vector>

using namespace std;
using namespace std::placeholders;

ChatService *ChatService::instance()
{
    static ChatService service;
    return &service;
}

void ChatService::reset()
{
    // 将所有在线用户的online状态重置为offline
    _userModel.resetState();
}

MsgHandler ChatService::getHandler(int msgid)
{
    if (_msgHandlerMap.find(msgid) == _msgHandlerMap.end())
    {
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp time)
        {
            LOG_ERROR << "msgid: " << msgid << " can not find handler!";
        };
    }
    else
    {
        return _msgHandlerMap[msgid];
    }
}

ChatService::ChatService()
{
    // 注册消息处理回调函数
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({CLIENT_QUIT_MSG, std::bind(&ChatService::loginout, this, _1, _2, _3)});
    _msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});

    // 连接redis服务器
    if (_redis.connect())
    {
        // 设置上报消息的回调
        _redis.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMsg, this, _1, _2));
    }
}

// 处理注册业务
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    // LOG_INFO << "do reg service!";
    string name = js["name"];
    string pwd = js["password"];

    User user;
    user.setName(name);
    user.setPwd(pwd);

    json response;

    bool state = _userModel.insert(user);
    if (state)
    {
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.getId();
    }
    else
    {
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;
    }
    conn->send(response.dump());
}

void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    // LOG_INFO << "do login service!";
    int id = js["id"];
    // string name = js["name"];
    string pwd = js["password"];
    json response;

    if (id == -1 || pwd.empty())
    {
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["errmsg"] = "无效的id或密码";
        conn->send(response.dump());
        return;
    }

    User user = _userModel.queryById(id);
    if (user.getId() == id && user.getPwd() == pwd)
    {
        if (user.getState() == "online")
        {
            // 用户已经登录，不允许重复登录
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "用户已经登录，无法重复登录";
        }
        else
        {
            // 登录成功
            // 0. id用户登录成功，向redis订阅channel
            _redis.subscribe(user.getId());

            // 1. 记录上线用户连接
            {
                std::lock_guard<mutex> lock(_connMutex);
                _userConnMap.insert({user.getId(), conn});
            }

            // 2. 需要更新用户状态信息
            user.setState("online");
            _userModel.updateState(user);

            // 3. 查看该用户是否有离线消息
            std::vector<std::string> ret = _offlineMsgModel.query(user.getId());
            if (!ret.empty())
            {
                response["offlinemsg"] = ret;

                // 读取完离线消息要及时删除
                _offlineMsgModel.remove(user.getId());
            }

            // 4. 给用户返回他的好友列表
            std::vector<User> vec = _friendModel.query(user.getId());
            if (!vec.empty())
            {
                std::vector<std::string> t;
                for (User &user : vec)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    t.push_back(js.dump());
                }
                response["friends"] = t;
            }

            // 5. 查询用户的群组信息
            std::vector<Group> groupuserVec = _groupModel.queryGroups(user.getId());
            if (!groupuserVec.empty())
            {
                std::vector<std::string> groupV;
                for (Group &group : groupuserVec)
                {
                    json groupjson;
                    groupjson["id"] = group.getId();
                    groupjson["groupname"] = group.getName();
                    groupjson["groupdesc"] = group.getDesc();

                    // 查询该group里面有哪些成员
                    std::vector<std::string> userV;
                    for (GroupUser &user : group.getUsers())
                    {
                        json userjson;
                        userjson["id"] = user.getId();
                        userjson["name"] = user.getName();
                        userjson["state"] = user.getState();
                        userjson["role"] = user.getRole();
                        userV.push_back(userjson.dump());
                    }
                    groupjson["users"] = userV;
                    groupV.push_back(groupjson.dump());
                }

                response["groups"] = groupV;
            }

            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["id"] = user.getId();
            response["name"] = user.getName();
        }
    }
    else
    {
        // 登录失败
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 3;
        response["errmsg"] = "用户id或密码错误!";
    }
    conn->send(response.dump());
}

void ChatService::loginout(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    // 正常退出
    int userid = js["id"].get<int>();
    {
        std::lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(userid);
        if (_userConnMap.end() != it)
        {
            _userConnMap.erase(it);
        }
    }

    // 用户注销，在redis中取消订阅
    _redis.unsubscribe(userid);

    // 更新用户信息
    User user(userid, "", "", "offline");
    _userModel.updateState(user);
}

void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    // json格式
    /*
        "msgid":6
        "id":myid
        "from":myname
        "to":peerid
        "msg":message
    */
    int toid = js["to"];
    {
        std::lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            // 对方在线
            it->second->send(js.dump());
            return;
        }
    }

    // 查询toid是否在线，如果在线，说明此时在别的服务器上登录着
    User user = _userModel.queryById(toid);
    if (user.getState() == "online")
    {
        _redis.publish(toid, js.dump());
        return;
    }

    // 对方不在线，存储离线消息
    _offlineMsgModel.insert(toid, js.dump());
}

void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();

    // 存储好友信息，好友是双向的(这里不做好友添加的验证了)
    _friendModel.insert(userid, friendid);
    _friendModel.insert(friendid, userid);
}

// 创建群组业务
void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    std::string name = js["groupname"];
    std::string desc = js["groupdesc"];

    // 创建群组
    Group group(-1, name, desc);
    if (_groupModel.createGroup(group))
    {
        // 存储创建者的信息
        _groupModel.addgroup(userid, group.getId(), "creator");
    }
}

// 加入群组业务
void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();

    _groupModel.addgroup(userid, groupid, "normal");
}

// 群组聊天业务
void ChatService::groupChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();

    // 查询属于groupid群组的所有用户id，然后查询连接表，如果在线直接发送，不在线就离线发送
    std::vector<int> userIdVec = _groupModel.queryGroupUsers(userid, groupid);

    std::lock_guard<std::mutex> lock(_connMutex);
    for (int &id : userIdVec)
    {
        auto it = _userConnMap.find(id);
        if (_userConnMap.end() != it)
        {
            // 该用户在线，直接转发
            it->second->send(js.dump());
        }
        else
        {
            // 查询toid是否在线，如果在线，说明此时在别的服务器上登录着
            User user = _userModel.queryById(id);
            if (user.getState() == "online")
            {
                _redis.publish(id, js.dump());
            }
            else
            {
                // 离线用户，转存为离线消息
                _offlineMsgModel.insert(id, js.dump());
            }
        }
    }
}

// 当不同服务器中订阅了channel后有消息到来，调用回调处理
void ChatService::handleRedisSubscribeMsg(int userid, std::string msg)
{
    lock_guard<mutex> lock(_connMutex);
    auto it = _userConnMap.find(userid);
    if (it != _userConnMap.end())
    {
        it->second->send(msg);
        return;
    }
    // 存储该用户的离线消息: 在从通道取消息时,用户下线则发送离线消息
    _offlineMsgModel.insert(userid, msg);
}

// 处理客户端用户异常退出
void ChatService::clientQuitException(const TcpConnectionPtr &conn, json js, Timestamp time)
{
    User user;

    // LOG_INFO << "客户端异常退出！";
    {
        std::lock_guard<mutex> lock(_connMutex);
        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); it++)
        {
            if (it->second == conn)
            {
                user.setId(it->first);
                _userConnMap.erase(it);
                break;
            }
        }
    }

    // 用户注销，在redis中取消订阅
    _redis.unsubscribe(user.getId());

    // 更新用户信息
    if (user.getId() != -1)
    {
        user.setState("offline");
        _userModel.updateState(user);
    }
}